(function (modules) {
  /**
   * 15 记录已经加载的chunk，三种状态
   *  0: 已经加载了
   *  [resolve, reject promise]: 加载中
   *  undefined: 还没加载
   */
  let installedChunks = {
    main: 0,
  };

  // jsonp 构建 script 标签的 src
  function jsonpScriptSrc(chunkId) {
    return __webpack_require__.p + '' + chunkId + '.built.js';
  }

  // 15 实现webpackJsonpCallback，合并模块定义，让 resolve全部执行，改变promise的状态，执行后续的行为
  function webpackJsonpCallback(data) {
    // 15.1 获取动态加载的 id
    let chunkIds = data[0];
    // 15.2 获取动态加载模块的依赖关系对象
    let moreModules = data[1];

    // 15.3 定义后续用到的变量
    let chunkId,
      resolves = [];
    // 15.4 循环判断 chunkIds 里面的模块有没有被加载
    for (let i = 0; i < chunkIds.length; i++) {
      chunkId = chunkIds[i];
      // 有值，并且 installedChunks[chunkId] 不是0
      if (Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {
        // 加进去的是 e 方法里面 给 installedChunks 赋值的
        // installedChunks[chunkId] = [resolve, reject, promise]
        resolves.push(installedChunks[chunkId][0]);
      }
      // 15.5 更新当前的 chunk加载状态 标记当前模块被加载了
      installedChunks[chunkId] = 0;
    }

    // 15.6 合并 模块到 modules
    for (moduleId in moreModules) {
      if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
        modules[moduleId] = moreModules[moduleId];
      }
    }

    // 15.7 将 resolves 变成成功态，因为 login.built.js 文件已经加载了
    // 这里是再走 login.built.js 文件里面的 push 操作的流程了
    // 然后全部完成之后就能表示懒加载完成了
    while (resolves.length) {
      resolves.shift()();
    }
  }

  // 01 缓存对象
  const installedModules = {};

  // 02 导入模块的方法 替换 import require
  function __webpack_require__(moduleId) {
    // 优先读缓存
    if (installedModules[moduleId]) {
      return installedModules[moduleId].exports;
    }

    // 初始化 模块对象，并且缓存
    let module = (installedModules[moduleId] = {
      i: moduleId,
      l: false,
      exports: {},
    });

    // 真正的加载步骤,调用 moduleId 对应的函数
    modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

    // 标记加载完成
    module.l = true;

    // 返回模块到调用的环境
    return module.exports;
  }

  // 03 定义 m 属性保存 modules
  __webpack_require__.m = modules;

  // 04 定义 c 属性保存 cache
  __webpack_require__.c = installedModules;

  // 05 o 方法判断对象上是否有某一个属性
  __webpack_require__.o = function (object, property) {
    return Object.prototype.hasOwnProperty(object, property);
  };

  // 06 定义 d 方法给对象上添加属性, 同时对这个属性提供一个 getter
  __webpack_require__.d = function (exports, name, getter) {
    if (!__webpack_require__.o(exports, name)) {
      Object.defineProperty(exports, name, { enumerable: true, get: getter });
    }
  };

  // 07 定义 r 方法，给 模块对象 身上添加执行一的属性，标记当前是 esm 规范环境
  __webpack_require__.r = function (exports) {
    if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
      Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
    }
    Object.defineProperty(exports, '__esModule', { value: true });
  };

  // 08 定义 n 方法，返回的对象提供一个 a 属性，用来兼容的给外界获取模块导出的值
  __webpack_require__.n = function (module) {
    let getter =
      // 三目运算 判断当前模块是什么环境
      module && module.__esModule
        ? function getDefault() {
            // 获取 ems 规范导出的值
            return module['default'];
          }
        : function getModuleExports() {
            // 获取 cms 规范导出的值
            return module;
          };
    // 是 给 getter 方法提供 a 属性并且把 getter 暴露出去
    __webpack_require__.d(getter, 'a', getter);
    return getter;
  };

  // 16 定义 e 方法，使用 jsonp 技术加载 chunk，使用promise实现异步加载
  __webpack_require__.e = function requireEnsure(chunkId) {
    // 16.1 定义一个数组存放 promise
    let promises = [];
    // 16.2 获取对应的 chunk
    let installedChunkData = installedChunks[chunkId];
    // 16.3 根据 chunk 的状态来进行后续的操作
    if (installedChunkData !== 0) {
      if (installedChunkData) {
        promises.push(installedChunkData[0]);
      } else {
        // 16.4 表示模块没有被加载
        // 16.5 promise技术进行异步加载，异步的提现是在 chunk.js文件中调用push方法的时候
        let promise = new Promise(function (resolve, reject) {
          installedChunkData = installedChunks[chunkId] = [resolve, reject];
        });
        promises.push((installedChunkData[2] = promise));

        // 使用 Jsonp 技术加载 login.built.js 文件
        let script = document.createElement('script');
        script.src = jsonpScriptSrc(chunkId);
        document.head.appendChild(script);
      }
    }

    // 执行 promise
    return Promise.all(promises);
  };

  // 09 加载 value 模块的内容，并且根据 mode 值对 value 进行处理后再返回
  __webpack_require__.t = function (value, mode) {
    // 加载value 并且重新赋值value
    if (mode & 1) value = __webpack_require__(value);
    // 返回可以直接使用的内容 (cms)
    if (mode & 8) return value;
    // 返回可以直接使用的内容 (ems)
    if (mode & 4 && typeof value === 'object' && value && value.__esModule) return value;
    // 需要定义 ns 处理 value 后在返回
    const ns = Object.create(null);
    // 标记是 ems 规范
    __webpack_require__.r(ns);
    // 添加 default 属性
    // Object.prototype.hasOwnProperty(Object.create(null),'default') -> 会报错
    Object.defineProperty(ns, 'default', { enumerable: true, value: value });
    // 这里是处理 ems 规范导出其他内容时候的情况
    if (mode & 2 && typeof value != 'string')
      for (var key in value)
        __webpack_require__.d(
          ns,
          key,
          function (key) {
            return value[key];
          }.bind(null, key)
        );
    // 最后返回 ns ,这个时候的 ns一定是一个 ems 规范的值
    return ns;
  };

  // 10 定义 p 属性，表示资源的访问路径
  __webpack_require__.p = '';

  // 12 定义变量 缓存数组
  let jsonpArray = (window['webpackJsonp'] = window['webpackJsonp'] || []);
  // 13 保存原生 push 方法
  let oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
  // 14 重写 jsonpArray 的 push 方法
  jsonpArray.push = webpackJsonpCallback;

  // 11 定义 s 属性，记录主入口
  // 11 调用 __webpack_require__ 开始加载主入口
  return __webpack_require__((__webpack_require__.s = './src/index.js'));
})(
  // 本节主要内容是实现功能函数， 模块定义的部分暂时不考虑如何生成的
  {
    './src/index.js': function (module, exports, __webpack_require__) {
      let btnEl = document.getElementById('btn');
      btnEl.addEventListener('click', function () {
        // 动态加载
        __webpack_require__
          .e('login')
          .then(__webpack_require__.t.bind(null, './src/login.js', 7))
          .then((loginTip) => {
            console.log('loginTip==>', loginTip);
          });
      });
      console.log('index.js执行了');
    },
  }
);
